<template>
    <div class="content" ref="content" id="content">
        <Loading v-if="!loadingPercentage.completed" size="220" color="#03b2c9" :text="loadingPercentage.percentage"
            dot-count="6" />
        <div ref="container" class="three-container"
            :style="{ width: `calc(100vw - ${containerOffsetLeft})`, height: `calc(100vh - ${containerOffsetTop})` }">
        </div>
    </div>
</template>

<script setup async>
import { ref, onMounted, onBeforeUnmount, defineEmits, defineProps, } from 'vue'
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass'
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass'
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
import { CSS2DRenderer, CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer"
import { gsap } from 'gsap'
import Loading from '@/components/Loading'
import { useFullscreen } from '@vueuse/core'
import 'animate.css/animate.min.css';
import textBackground from '@/assets/three/text-bac.svg';
import lineBackground from '@/assets/three/line.svg';
import AnimationsModule from '@/utils/three/animations.js'
import { Group, Tween, Easing } from '@tweenjs/tween.js'

const props = defineProps({
    outlinePass: {
        type: Boolean,
        default: false
    }
})
const emit = defineEmits(['loadingFinish']);
// DOM 引用
const container = ref(null)
const containerOffsetLeft = ref('200px');
const containerOffsetTop = ref('85');

const content = ref(null)
const { toggle } = useFullscreen(content)

// 1. 创建独立的tween组
const tweenGroup = new Group()

const loadingPercentage = ref({
    percentage: '',
    completed: false
})

// Three.js 核心对象
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 70000)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

let renderer = null;
let controls = null;
let composer = null;
let css2Renderer = null;
const gltfGroup = new THREE.Group()
const css2dRendererGroup = new THREE.Group()
const boundingPlanesGroup = new THREE.Group()
let outlinePass;
let animationss;

// 资源管理
const resources = {
    textures: [],
    models: [],
    geometries: [],
    materials: [],
    eventListeners: [],
    animations: []
}

defineExpose([
    scene,
    camera,
    renderer,
])
// 模型配置
const gltfArray = [
    // { url: '/石带水厂/jixiufang_result-0.gltf', labelText: '机修房', isBbox: false },
    // { url: '/石带水厂/jiayaojian_result-0.gltf', labelText: '加药间', isBbox: false },
    // { url: '/石带水厂/chendianchi_result-0.gltf', labelText: '沉淀池', isBbox: false },
    // { url: '/石带水厂/nonsuochi_result-0.gltf', labelText: '浓缩池', isBbox: false },
    // { url: '/石带水厂/painichi_result-0.gltf', labelText: '排泥池', isBbox: false },
    // { url: '/石带水厂/qingshuichi_result-0.gltf', labelText: '清水池', isBbox: false },
    // { url: '/石带水厂/shitang_result-0.gltf', labelText: '食堂', isBbox: false },
    // { url: '/石带水厂/tuoshuijifang_result-0.gltf', labelText: '跳水车间', isBbox: false },
    { url: '/石带水厂/Vxinglvchi-0.gltf', labelText: 'V型滤池', isBbox: false },
    // { url: '/石带水厂/changdi.gltf', labelText: '', isBbox: false },
    // { url: '/石带水厂/pinghengchi_result-0.gltf', labelText: '平衡池', isBbox: false },
    // { url: '/石带水厂/shidai_zohelou.glb', labelText: '综合楼', isBbox: false },
    // // { url: 'public/门卫及场地/门卫及场地_result-0.gltf', labelText: '', isBbox: false },
    // // { url: 'public/门卫及场地/门卫及场地_result-1.gltf', labelText: '', isBbox: false },
    // // { url: 'public/门卫及场地/门卫及场地_result-2.gltf', labelText: '', isBbox: false },


    // { url: '/GLTF/GLTF/【加药间-机电】/【加药间-机电】_result-0.gltf', labelText: '', isBbox: false },
    // { url: '/GLTF/GLTF/【平衡池-机电】/【平衡池-机电】_result-0.gltf', labelText: '', isBbox: false },
    // { url: '/V型滤池[机电管道]/V型滤池[机电管道]_result-0.gltf', labelText: '', isBbox: false },


]
// 加载管理器
const loadingManager = new THREE.LoadingManager(
    () => {

        loadingPercentage.value.completed = true;
        emit('loadingFinish', scene);
        let module = gltfGroup.children[0];
        setCameraTarget(module);
        animationss.play(module.resetName, module.animationsDuration);
    },
    (url, loaded, total) => {
        loadingPercentage.value.percentage = `加载中..${((loaded / total) * 100).toFixed(2)}%`
    }
)

// 设置相机目标
const setCameraTarget = (model) => {
    const box = new THREE.Box3().setFromObject(model)
    const center = box.getCenter(new THREE.Vector3())
    const size = box.getSize(new THREE.Vector3())
    const targetPosition = new THREE.Vector3(
        center.x + size.length() * 0.5,
        center.y + size.length() * 0.6,
        center.z + size.length() * 0.5
    )
    // 相机位置动画
    const cameraTween = new Tween(camera.position, tweenGroup)
        .to(targetPosition, 1500)
        .easing(Easing.Quartic.InOut)
        .onUpdate(() => controls?.update())
        .start().onComplete(() => tweenGroup.remove(cameraTween));

    // 控制目标动画
    const targetTween = new Tween(controls.target, tweenGroup)
        .to(center, 800)
        .easing(Easing.Quartic.InOut)
        .start().onComplete(() => tweenGroup.remove(targetTween));
}

// 初始化场景
const initScene = async () => {
    // 初始化渲染器
    renderer = new THREE.WebGLRenderer({
        antialias: true,
        logarithmicDepthBuffer: true
    })
    updateRendererSize()
    container.value.appendChild(renderer.domElement)

    camera.position.set(0, 300, 300)
    camera.updateProjectionMatrix()

    // 初始化控制器
    controls = new OrbitControls(camera, renderer.domElement)
    controls.enableDamping = true

    // 设置场景背景
    scene.background = new THREE.Color('#02004d')

    // 初始化后期处理和标签渲染器
    initPostProcessing()
    createCSS2DRenderer()

    // 加载模型和环境
    gltfArray.forEach((item) => {
        loadModel(item)
    })

    loadEnvironment()

    // 加载模型动画集合
    animationss = new AnimationsModule({ tweenGroup, Tween, Easing });

    scene.add(gltfGroup)
    scene.add(css2dRendererGroup)
    scene.add(boundingPlanesGroup)
}

// 创建标签
const createLabel = (text) => {
    const label = document.createElement("div")
    label.style.cursor = 'pointer';
    label.style.pointerEvents = 'auto';
    label.innerHTML = `
        <div class='animate__animated animate__bounceIn' style="display: flex;align-items: center;flex-direction: column;justify-content: center;">
            <div style="width:120px;height: 50px;position: relative;">
                <img src="${textBackground}" style="width:120px;height: 50px;" />
                <div class="label-text" style="position:absolute;top:11.5px;left:0;right:0;margin: 0 auto;text-align: center;color:#fff;">${text}</div>    
            </div>
            <img src="${lineBackground}" style="width: 3px;height: 40px;margin-top: -5px;" />    
        </div>
    `
    return label
}

// 创建CSS2D渲染器
const createCSS2DRenderer = () => {
    css2Renderer = new CSS2DRenderer()
    updateCSS2DRendererSize()
    css2Renderer.domElement.style.position = 'absolute'
    css2Renderer.domElement.style.top = '0'
    css2Renderer.domElement.style.pointerEvents = 'none'
    container.value.appendChild(css2Renderer.domElement)
}

// 初始化后期处理
const initPostProcessing = () => {
    composer = new EffectComposer(renderer)

    // 添加RenderPass（带色调映射）
    const renderPass = new RenderPass(scene, camera);
    renderPass.toneMapping = renderer.toneMapping;
    renderPass.toneMappingExposure = renderer.toneMappingExposure;
    composer.addPass(renderPass);

    const fxaaPass = new ShaderPass(FXAAShader)
    const pixelRatio = renderer.getPixelRatio()
    fxaaPass.material.uniforms['resolution'].value.x = 1 / (container.value.clientWidth * pixelRatio)
    fxaaPass.material.uniforms['resolution'].value.y = 1 / (container.value.clientHeight * pixelRatio)
    composer.addPass(fxaaPass)

    const outputPass = new OutputPass();
    outputPass.toneMapping = THREE.NoToneMapping; // 避免二次处理
    outputPass.outputColorSpace = THREE.sRGBColorSpace; // r152+
    composer.addPass(outputPass);

    if (props.outlinePass) {
        // 4. 创建描边效果
        outlinePass = new OutlinePass(
            new THREE.Vector2(container.value.clientWidth, container.value.clientHeight),
            scene,
            camera
        );
        outlinePass.edgeStrength = 3.0; // 描边强度
        outlinePass.edgeGlow = 0.5;     // 边缘发光
        outlinePass.edgeThickness = 1.0; // 边缘厚度
        outlinePass.pulsePeriod = 2;    // 脉冲周期
        outlinePass.visibleEdgeColor.set('#ff0000'); // 可见边颜色
        outlinePass.hiddenEdgeColor.set('#ff0000');  // 隐藏边颜色
        composer.addPass(outlinePass);
        window.addEventListener('mousemove', onMouseMove);
    }
}


function onMouseMove(event) {
    // 获取容器相对于视口的坐标位置
    const rect = container.value.getBoundingClientRect();

    // 计算容器内的相对鼠标坐标 (考虑滚动偏移)
    const mouseX = event.clientX - rect.left;
    const mouseY = event.clientY - rect.top;

    // 转换为标准化设备坐标 (NDC: -1到1)
    mouse.x = (mouseX / rect.width) * 2 - 1;
    mouse.y = -(mouseY / rect.height) * 2 + 1;

    // 更新射线检测
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(gltfGroup.children);
    // 更新描边对象
    outlinePass.selectedObjects = intersects.length > 0 ? [intersects[0].object.parent] : [];
}


// 加载环境贴图
const loadEnvironment = () => {
    const hdrLoader = new RGBELoader()
    hdrLoader.load('/golden_gate_hills_4k.hdr', (texture) => {
        texture.mapping = THREE.EquirectangularReflectionMapping
        scene.environment = texture
        resources.textures.push(texture)
    })
}
// 加载模型
const loadModel = ({ url, labelText, isBbox, sg }) => {
    const loader = new GLTFLoader(loadingManager)
    const dracoLoader = new DRACOLoader()
    dracoLoader.setDecoderPath("/draco/gltf/")
    dracoLoader.setDecoderConfig({ type: "js" })
    loader.setDRACOLoader(dracoLoader)

    loader.load(url, (gltf) => {
        const model = gltf.scene
        resources.models.push(model)

        if (labelText) {
            const box = new THREE.Box3().setFromObject(model)
            const center = box.getCenter(new THREE.Vector3())
            const div = createLabel(labelText)

            const labelObject = new CSS2DObject(div)
            labelObject.position.set(center.x, box.max.y, center.z)
            labelObject.center.set(0.5, 1);
            labelObject.resetName = labelText
            labelObject.visible = false;
            css2dRendererGroup.add(labelObject)
            resources.models.push(labelObject)
        }
        // 获取动画
        const animations = gltf?.animations;
        if (animations && animations.length > 0) {
            // 创建动画混合器
            const mixer = new THREE.AnimationMixer(model);
            // 播放所有动画
            animations.forEach((clip) => {
                const action = mixer.clipAction(clip);
                action.play();
                // 关键设置：只播放一次
                action.setLoop(THREE.LoopRepeat);
                // 保持在最后一帧
                action.clampWhenFinished = true;
            });
            animationss.add(labelText, mixer);
            model.animationsDuration = animations[0]?.duration || 1.25;
        }
        model.resetName = labelText
        gltfGroup.add(model)
    }, (error) => {

    })
}

// 更新渲染器尺寸
const updateRendererSize = () => {
    if (!container.value) return

    const width = container.value.clientWidth
    const height = container.value.clientHeight

    camera.aspect = width / height
    camera.updateProjectionMatrix()
    renderer?.setSize(width, height)

    // 更新后期处理器
    if (composer) {
        composer.setSize(width, height)
        const passes = composer.passes
        for (let i = 0; i < passes.length; i++) {
            if (passes[i] instanceof ShaderPass && passes[i].material.uniforms['resolution']) {
                const pixelRatio = renderer.getPixelRatio()
                passes[i].material.uniforms['resolution'].value.x = 1 / (width * pixelRatio)
                passes[i].material.uniforms['resolution'].value.y = 1 / (height * pixelRatio)
            }
        }
    }
}

// 更新CSS2D渲染器尺寸
const updateCSS2DRendererSize = () => {
    if (!container.value || !css2Renderer) return
    const width = container.value.clientWidth
    const height = container.value.clientHeight
    css2Renderer.setSize(width, height)
}

// 主动画循环
const animate = () => {
    if (!container.value) return

    const rect = container.value.getBoundingClientRect()
    if (rect) {
        containerOffsetLeft.value = rect.left + 'px';
        containerOffsetTop.value = rect.top + 'px';
        renderer?.setSize(rect.width, rect.height)
        camera.aspect = rect.width / rect.height
        camera.updateProjectionMatrix()
        css2Renderer?.setSize(rect.width, rect.height)
        css2Renderer?.render(scene, camera)
    }

    composer?.render()
    controls?.update();
    requestAnimationFrame(animate)


    // 更新 Tween 动画
    tweenGroup.update();
}

// 窗口大小变化处理
const handleResize = () => {
    updateRendererSize()
    updateCSS2DRendererSize()
}

// 组件挂载
onMounted(() => {
    try {
    } catch (e) {
    }


    initScene()
    animate()

    window.addEventListener('resize', handleResize)
})

// 组件卸载前清理
onBeforeUnmount(() => {
    // 移除事件监听
    window.removeEventListener('resize', handleResize)
    window.removeEventListener('mousemove', onMouseMove);

    // 清理动画
    resources.animations.forEach(anim => anim.kill())

    // 清理事件监听器
    resources.eventListeners.forEach(({ element, handler }) => {
        element?.removeEventListener('click', handler)
    })

    // 清理场景
    scene.traverse(object => {
        if (!object.isMesh) return

        if (object.geometry) {
            object.geometry.dispose()
        }

        if (object.material) {
            if (Array.isArray(object.material)) {
                object.material.forEach(m => m.dispose())
            } else {
                object.material.dispose()
            }
        }
    })
    // 清理纹理
    resources.textures.forEach(texture => {
        texture.dispose()
        texture.image?.close?.()
    })

    // 清理渲染器
    if (renderer) {
        container.value?.removeChild(renderer.domElement)
        renderer.dispose()
    }

    // 清理CSS2D渲染器
    if (css2Renderer) {
        container.value?.removeChild(css2Renderer.domElement)
    }

    // 清理控制器
    controls?.dispose();

    // 清理GSAP
    gsap.globalTimeline.clear();

    tweenGroup.removeAll();

})
</script>

<style scoped lang="scss">
.three-container {
    width: 100vw;
    height: 100vh;
    position: relative;
    overflow: hidden;
}

.content {
    width: 100%;
    height: 100%;
    position: relative;
}
</style>